#include "spotlightsource.h"
#include <cmath>
#include <GL/gl.h>
#include "matrix.h"

#define A_PARAM 0.2
#define B_PARAM 0.2
#define C_PARAM 0.2

SpotLightSource::SpotLightSource(const Point &source, const Point &lookAt, double angle, double atenuation_factor)
    : source_(source), lookAt_(lookAt), direction_(lookAt - source),
    angle_(angle), atenuation_factor_(atenuation_factor)
{
    direction_.normalize();
}

LightIntensity SpotLightSource::intensity(const Point &p) const
{
    LightIntensity it;
    Vector s = p - source_;
    double distance = s.norm();
    const double distance_atenuation = 1.0 / ( A_PARAM + B_PARAM*distance + C_PARAM*distance*distance);
    const double angle_atenuation = pow(direction_ * s.normalize(), atenuation_factor_);
    const double total_atenuation = distance_atenuation * angle_atenuation;

    it.set_ambient(this->ambient());
    it.set_diffuse(total_atenuation * this->diffuse());
    it.set_specular(total_atenuation * this->specular());

    return it;
}

Vector SpotLightSource::direction(const Point &p) const
{
    Vector d = source_ - p;
    return d.normalize();
}

void SpotLightSource::glSetup(int light_id) const
{
    GLfloat light_pos[] = {source_(0), source_(1), source_(2), 1.0};
    GLfloat light_dir[] = {direction_(0), direction_(1), direction_(2), 0.0};
    GLfloat gl_ambient[] = {ambient().r(), ambient().g(), ambient().b(), 1.0};
    GLfloat gl_diffuse[] = {diffuse().r(), diffuse().g(), diffuse().b(), 1.0};
    GLfloat gl_specular[] = {specular().r(), specular().g(), specular().b(), 1.0};

    glLightfv(light_id, GL_POSITION, light_pos);
    glLightfv(light_id, GL_AMBIENT, gl_ambient);
    glLightfv(light_id, GL_DIFFUSE, gl_diffuse);
    glLightfv(light_id, GL_SPECULAR, gl_specular);
    glLightfv(light_id, GL_SPOT_DIRECTION, light_dir);
    glLightf(light_id, GL_SPOT_CUTOFF, angle_);
    glLightf(light_id, GL_SPOT_EXPONENT, atenuation_factor_);
}

void SpotLightSource::applyCameraTransformation(const Camera &camera)
{
    Matrix transf = Matrix::coordChangeMatrix(camera.frame());
    source_ = transf * source_;
    lookAt_ = transf * lookAt_;
    direction_ = lookAt_ - source_;
    direction_.normalize();
}

ILightSource* SpotLightSource::clone() const
{
    SpotLightSource *light = new SpotLightSource(source_, lookAt_, angle_, atenuation_factor_);
    light->intensities_ = this->intensities_;
    return light;
}
#undef A_PARAM
#undef B_PARAM
#undef C_PARAM
